package com.athui.utils.common.jdbc;

import com.alibaba.druid.pool.DruidDataSource;
import com.athui.bean.config.ConnectInfo;


import javax.sql.DataSource;
import java.io.Serializable;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @description: TODO
 * @autor: zhangzhonghui
 * @create: 2024-08-06 08:53
 * @Version: 1.0
 */
public class JdbcUtils implements Serializable {

    /**
     * @description: TODO 数据库连接信息
     * @autor: zhangzhonghui
     * @create: 2024/8/2 21:02
     * @Version: 1.0
     **/
    private ConnectInfo connectInfo;
    /**
     * @description: TODO  数据源
     * @autor: zhangzhonghui
     * @create: 2024/8/2 21:02
     * @Version: 1.0
     **/
    private DruidDataSource druidDataSource;

    /**
     * @description: TODO 获取数据库连接
     * @autor: zhangzhonghui
     * @return: java.sql.Connection
     * @create: 2024/8/2 21:23
     * @Version: 1.0
     **/
    protected Connection getConnection() throws SQLException, ClassNotFoundException {
        DataSource dataSource = getDataSource(connectInfo);
        Connection connection = dataSource.getConnection();
        if(connection.isClosed()){
            System.out.println("Connection is closed ...");
        }
        return connection;
    }

    /**
     * @description: TODO JDBC query 操作，返回 T 对象
     * @author: 阿辉大人
     * @param: resultSetBuilder
     * @return: T
     * @create: 2024/8/15 17:45
     * @version: 1.0
    **/
    public  <T> T operation(
            JdbcResultSetBuilder<T> resultSetBuilder) {
        Connection connection =null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try{
            connection = getConnection();
            preparedStatement = connection.prepareStatement(resultSetBuilder.buildSql());
            resultSet = preparedStatement.executeQuery();
            return resultSetBuilder.accept(resultSet);
        } catch (SQLException | ClassNotFoundException throwables) {
            throwables.printStackTrace();
        } finally {
            close(connection,preparedStatement,resultSet);
        }
        return null;
    }

    /**
     * @description: TODO jdbc 增删改 操作，返回 布尔操作
     * @author: 阿辉大人
     * @param: statementBuilder
     * @param: t
     * @return: java.lang.Boolean
     * @create: 2024/8/15 17:46
     * @version: 1.0
    **/
    public <T> Boolean operation(JdbcStatementBuilder<T> statementBuilder,T t){
        List<T> dataList = new ArrayList<>();
        dataList.add(t);
        return operation(statementBuilder,dataList,10);
    }
    public <T> boolean operation(JdbcStatementBuilder<T> statementBuilder, List<T> dataList) {
        return operation(statementBuilder,dataList,100);
    }

    public <T> boolean operation(JdbcStatementBuilder<T> statementBuilder, List<T> dataList,int batchSize) {
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            connectInfo = statementBuilder.buildConnectInfo();
            connection = getConnection();
            ps = connection.prepareStatement(statementBuilder.buildSql());

            boolean flag = true;
            for (int i = 0; i < dataList.size(); i++) {
                statementBuilder.accept(ps, dataList.get(i));
                ps.addBatch();
                if (i+1 % batchSize == 0) {
                    ps.executeBatch(); // 执行批处理
                    ps.clearBatch(); // 清空批处理
                    flag=false;
                    continue;
                }
                flag =true;
            }

            if(flag){
                // 表示有剩余的数据
                ps.executeBatch(); // 执行批处理
                ps.clearBatch(); // 清空批处理
            }
            return true;
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            close(connection,ps);
        }
        return false;
    }

    protected void close(PreparedStatement preparedStatement, ResultSet resultSet) {
        close(null,preparedStatement,resultSet);
    }
    protected void close(Connection connection,PreparedStatement preparedStatement) {
        close(connection,preparedStatement,null);
    }

    protected void close(Connection connection,ResultSet resultSet) {
        close(connection,null,resultSet);
    }

    /**
     * @description: TODO 释放资源
     * 若关闭过程中出现SQLException异常，则打印异常堆栈信息。
     * 该函数确保在数据库操作完成后，能够正确关闭数据库连接和相关资源，避免资源泄露。
     * @autor: zhangzhonghui
     * @param: connection 如果connection不为空，则关闭它；
     * @param: preparedStatement 如果preparedStatement不为空，则关闭它；
     * @param: resultSet 如果resultSet不为空，则关闭它；
     * @create: 2024/8/1 14:18
     * @Version: 1.0
     **/
    protected void close(Connection connection,PreparedStatement preparedStatement,ResultSet resultSet)  {
        try{
            if(resultSet != null){
                resultSet.close();
            }
            if(preparedStatement != null){
                preparedStatement.close();
            }
            if(connection != null){
                connection.close();
            }
        }catch(SQLException e ){
            e.printStackTrace();
        }

    }

    /**
     * @description: TODO 获取 DataSource
     * @autor: zhangzhonghui
     * @param: jdbc jdbc url
     * @param: username 账号
     * @param: password 密码
     * @return: javax.sql.DataSource
     * @create: 2024/8/1 13:41
     * @Version: 1.0
     **/
    private DataSource getDataSource(ConnectInfo connectInfo) throws SQLException {
        if (druidDataSource == null) {
            Properties props = new Properties();
            props.setProperty("driverClassName",connectInfo.getDriver().toString());
            props.setProperty("remarksReporting","true"); // oracle 获取 表注释为 null，需要配置此参数

            this.druidDataSource = new DruidDataSource();
            // 基本信息
            druidDataSource.setUrl(connectInfo.getJdbc());

            druidDataSource.setUsername(connectInfo.getUsername());
            druidDataSource.setPassword(connectInfo.getPassword());
            druidDataSource.setConnectProperties(props);

            // 配置初始化大小、最小、最大
            druidDataSource.setInitialSize(1);
            druidDataSource.setMinIdle(1);
            druidDataSource.setMaxActive(3);

            // 配置获取连接等待超时的时间
            druidDataSource.setMaxWait(60000L);

            // 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒
            druidDataSource.setTimeBetweenEvictionRunsMillis(2000L);

            // 配置一个连接在池中最小生存的时间，单位是毫秒
            druidDataSource.setMinEvictableIdleTimeMillis(600000L);
            druidDataSource.setMaxEvictableIdleTimeMillis(900000L);

            // druidDataSource.setValidationQuery("select 1");
            druidDataSource.setTestWhileIdle(true);
            druidDataSource.setTestOnBorrow(false);
            druidDataSource.setTestOnReturn(false);

            druidDataSource.setKeepAlive(true);
            druidDataSource.setPhyMaxUseCount(500);

//            // 是否自动回收超时连接
//            druidDataSource.setRemoveAbandoned(true);
//            // 超时时间，以秒为单位
//            druidDataSource.setRemoveAbandonedTimeout(180);
//            // 设置为 true 时，不会一直重连，否则会一直重连并打印错误日志
//            druidDataSource.setBreakAfterAcquireFailure(true);
        }

        return this.druidDataSource;
    }

    public void putConnectInfo(ConnectInfo connectInfo) {
        this.connectInfo = connectInfo;
    }
}
